# coding=utf-8
import logging
from functools import lru_cache

from selenium import webdriver
import time
from datetime import datetime, timedelta
import threading

import datetime
from dateutil.relativedelta import relativedelta
BASE_DATE_FORMAT = "%Y-%m-%d"
NOT_SPILT_DATE_FORMAT = '%Y%m%d'
YEAR_MONTH_DATE_FORMAT = "%Y%m"

@lru_cache(maxsize=10)
def dateRange(start, end, step=1, format="%Y%m%d", type = "d"):
    """ [start, end )
        :arg
         type: d 天
            m 月"""
    start = str(start)
    end = str(end)
    strptime, strftime = datetime.datetime.strptime, datetime.datetime.strftime
    if strptime(end, format) < strptime(start, format):
        return list()

    def month_diff(x,y):
        return abs((x.year - y.year)* 12 + (x.month - y.month)* 1)
    _delta = ''
    gap = ''
    if type == 'd':
        def funcd(d):
            return relativedelta(days = d)

        gap = (strptime(end, format) - strptime(start, format)).days
        _delta = funcd
    elif type == 'm':
        def funcm(d):
            return relativedelta(months = d)
        _delta = funcm
        gap = month_diff(strptime(end, format),strptime(start, format))
    else:
        logging.exception("unexcepted type ")
        return

    return [strftime(strptime(start, format) + _delta(i), format) for i in range(0, gap, step)]

@lru_cache(maxsize=10)
def dateRange1(start, end, step=1, format="%Y%m%d", type = "d", cont_l  = True, cont_r = True):
    """ [start, end )
        :arg
         type: d 天
            m 月"""
    start = str(start)
    end = str(end)
    strptime, strftime = datetime.datetime.strptime, datetime.datetime.strftime
    if strptime(end, format) < strptime(start, format):
        return list()

    def month_diff(x,y):
        return abs((x.year - y.year)* 12 + (x.month - y.month)* 1)
    _delta = ''
    gap = ''
    if type == 'd':
        def funcd(d):
            return relativedelta(days = d)

        gap = (strptime(end, format) - strptime(start, format)).days
        _delta = funcd
    elif type == 'm':
        def funcm(d):
            return relativedelta(months = d)
        _delta = funcm
        gap = month_diff(strptime(end, format),strptime(start, format))
    else:
        logging.exception("unexcepted type ")
        return

    return [strftime(strptime(start, format) + _delta(i), format) for i in range(0 if not cont_l else 1, gap + (0 if not cont_r else 1), step)]
@lru_cache(maxsize=10)
def dateRange_date(start:datetime.date, end:datetime.date, step=1,  type = "d", cont_l  = True, cont_r = True):
    """ [start, end )
        :arg
         type: d 天
            m 月"""
    if end<start:
        return list()
    def month_diff(x,y):
        return abs((x.year - y.year)* 12 + (x.month - y.month)* 1)
    _delta = ''
    gap = ''
    if type == 'd':
        def funcd(d):
            return relativedelta(days = d)

        gap = (end- start).days
        _delta = funcd
    elif type == 'm':
        def funcm(d):
            return relativedelta(months = d)
        _delta = funcm
        gap = month_diff(end,start)
    else:
        logging.exception("unexcepted type ")
        return

    return [start+_delta(i) for i in range(0 if not cont_l else 1, gap + (0 if not cont_r else 1), step)]

def getCookieFromNormalResponse(resp):
    cookies = resp.cookies
    _cook_st = ''
    if cookies:
        for cookie in cookies:
            _cook_st += '{}={}; '.format(cookie.name, cookie.value)

    return _cook_st

def substring(string, start=0, end=-1):
    if string is not None:
        return string[start: end]
    return ""

class switch(object):

    def __init__(self, value):
        self.value = value
        self.fall = False


    def __iter__(self):
        yield self.match
        raise StopIteration


    def match(self, *args):
        if self.fall or not args:
            return True
        elif self.value in args:
            self.fall = True
            return True
        else:
            return False


def get_dom_node_text(item, index_list):
    if (item is None) or (len(index_list) == 0):
        return ""

    for index in index_list:
        if len(item.childNodes) <= index:
            return ""

        item = item.childNodes[index]

    if (item is not None) and (item.nodeType == 3):
        return item.wholeText
    elif (item is not None) and (item.nodeType == 1):
        return item.childNodes[0].wholeText

    return ""

def get_dom_node(item, index_list):
    if (item is None) or (len(index_list) == 0):
        return None

    for index in index_list:
        if len(item.childNodes) <= index:
            return None

        item = item.childNodes[index]

    if item is not None:
        return item

    return None


def get_dom_node_value(item, index_list):
    value_str = get_dom_node_text(item, index_list)
    value_str = value_str.replace(",", "")
    value_str = value_str.replace("-", "")
    if value_str is not "":
        return round(float(value_str), 4)

    return 0


def get_dom_node_percent_value(item, index_list):
    value_str = get_dom_node_text(item, index_list)
    value_str = value_str.replace(",", "")
    value_str = value_str.replace("-", "")
    if value_str is not "":
        value_str = value_str.strip("%")
        return round(float(value_str) / 100, 4)

    return 0


def get_dom_node_star(item, index_list):
    value_str = get_dom_node_text(item, index_list)

    if value_str is not "":
        return value_str.count('★')

    return 0


def decode_response_text(string):
    page = ""
    page_code = ""
    if len(string) == 0:
        return page, page_code

    try:
        page_code = "utf8"
        page = string.decode("utf8")
    except UnicodeDecodeError as e:
        error_message = 'utf-8'
        if (e is not None) and (e.args is not None) and e.args[0].startswith(error_message,
                                                                                 0,
                                                                                 len(error_message)):
            try:
               page_code = "gb2312"
               page = string.decode("gb2312")
            except UnicodeDecodeError as e:
                error_message = 'gb2312'
                if (e is not None) and (e.args is not None) and e.args[0].startswith(error_message,
                                                                                         0,
                                                                                         len(error_message)):
                    page_code = "gbk"
                    page = string.decode("gbk")
                else:
                    raise e
            finally:
                pass
        else:
            raise e
    finally:
        pass

    return page, page_code


# 获取动态cookies
class CookieObject:
    _instance_lock = threading.Lock()
    cookie_object = None
    cookie_url = ''
    lock = threading.Lock()

    def __init__(self, url=''):
        self.cookie_url = url

    def reset_cookie(self, url=''):
        with self.lock:
            if len(url) > 0:
                self.cookie_url = url
            if self.cookie_object is None:
                return
            self.cookie_object = None

    def get_cookie(self):
        return self.cookie_object

    def get_cookie_value(self):
        cookie_string = ""
        index = 0
        while index < len(self.cookie_object):
            item = self.cookie_object[index]
            cookie_string += item['name'] + "=" + item['value'] + "; "
            index += 1
        return cookie_string

    def get_cookie_item(self, name=''):
        cookie_string = ""
        index = 0
        while index < len(self.cookie_object):
            item = self.cookie_object[index]
            if item['name'] == name:
                cookie_string = item['value']
                break
            index += 1
        return cookie_string

    def refresh_cookie(self):
        with self.lock:
            options = webdriver.ChromeOptions()
            options.add_argument('headless')
            driver = webdriver.Chrome(chrome_options=options)
            driver.get(self.cookie_url)
            # 获取cookie列表
            self.cookie_object = driver.get_cookies()
            driver.close()


def get_year_first_day_string(year=2019):
    return str(year) + "-01-01"


def get_year_last_day_string(year=2019):
    return str(year) + "-12-31"


def get_ajacent_date_string(date='2019-01-01', format="%Y-%m-%d", next_date=True):
    base_time = time.strptime(date, format )
    ajacent_day = datetime(base_time.tm_year, base_time.tm_mon, base_time.tm_mday)
    if next_date:
        ajacent_day += timedelta(days=1)
    else:
        ajacent_day -= timedelta(days=1)
    ajacent_day_string = ajacent_day.strftime(format)
    return ajacent_day_string


def stock_price_round(number, position=2):
    str_format = ":.{0}f".format(position+1)
    str_format = "{" + str_format + "}"
    num_str = str_format.format(number)
    dot_pos = num_str.find('.')
    if dot_pos == -1:
        return number

    tail = num_str[dot_pos + position + 1:dot_pos + position + 2]
    if tail != '5':
        return round(number, position)

    temp = num_str[:dot_pos + position + 1] + '6' + num_str[dot_pos + position + 2:]
    return round(float(temp), position)


def calculate_reach_limit(pre_close, close, high=True):
    if high:
        # 计算是否当日涨停
        if pre_close == 0 or close <= pre_close:
            return 0
        result = stock_price_round(pre_close*1.1, 2)
        if close >= result:
            return 1
        else:
            return 0
    else:
        # 计算是否当日跌停
        if pre_close == 0 or close >= pre_close:
            return 0
        result = stock_price_round(pre_close*0.9, 2)
        if close <= result:
            return 1
        else:
            return 0


def calculate_reach_ratio(pre_close, price, ratio=-5):
    reference_price = pre_close * (1 + round(float(ratio)/100, 2))
    if ratio > 0:
        return price >= reference_price
    else:
        return price <= reference_price


def calculate_limit(pre_close, high=True):
    if high:
        return stock_price_round(pre_close*1.1, 2)
    else:
        return stock_price_round(pre_close*0.9, 2)


def calculate_date_gap(trade_date_list, last_trade_date, current_trade_date):
    if len(last_trade_date) == 0 or len(current_trade_date) == 0 or len(trade_date_list) == 0:
        return 0

    if (last_trade_date in trade_date_list) and (current_trade_date in trade_date_list):
        index1 = trade_date_list.index(last_trade_date)
        index2 = trade_date_list.index(current_trade_date)
        return abs(index2 - index1)

    return 0


def get_today_timestamp():
    date = datetime.date.today()
    start = (date.year, date.month, date.day, 0, 0, 0, 0, 0, 0)
    end = (date.year, date.month, date.day, 23, 59, 59, 0, 0, 0)
    start_stamp = time.mktime(start)
    end_stamp = time.mktime(end)
    return start_stamp, end_stamp

